//#include <wx/log.h>
#include "AMFReader.h"
#include "wxAMF.h"
//#include <memory>
#include <wx/log.h>

AMFReader::AMFReader(wxInputStream &input)
{
    _input = &input;
    //ctor
}

AMFReader::~AMFReader()
{
    //dtor
    //wxLogMessage(wxString::Format(_("%d"), _stringReference.GetCount()));
	//_stringReference.Clear();
}

AMFMessage AMFReader::ReadAMFMessage()
{
    AMFMessage msg;
    wxUint16 version = ReadUInt16();
    msg._version = version == 0 ? AMF0 : AMF3;
    wxUint16 headerCount = ReadUInt16();
    wxUint16 bodyCount = ReadUInt16();
    for (wxUint16 i = 0; i < bodyCount; i++) {
        AMFBody *body = new AMFBody();
        body->_response = ReadString();
        body->_target = ReadString();
        wxUint32 len = ReadUInt32();
        body->_content = ReadAMFData();
        msg.AddBody(body);
    }
    return msg;
}

AMFObject *AMFReader::ReadAMFData()
{
    wxByte buff;
    _input->Read(&buff, 1);
    AMFObject *obj = new AMFObject();
    obj->_amf3 = false;
    obj->_type = buff;
    if(obj->_type == AMF0DataType::String) throw _("尚未支援AMF0_String資料型態讀取");
    else if(obj->_type == AMF0DataType::Array) {
        obj->_content = ReadAMFArray();
    }
    else if(obj->_type == AMF0DataType::AMF3Data) {
        obj->_content = ReadAMF3Data();
    }
    else throw wxString::Format(_T("尚未支援AMF0的'%d'資料型態讀取"), obj->_type);
    return obj;
}

AMFObject* AMFReader::ReadAMF3Data()
{
    wxByte buff;
    _input->Read(&buff, 1);
    AMFObject *obj = new AMFObject();
    obj->_amf3 = true;
    obj->_type = buff;
    if(obj->_type == AMF3DataType::Null)obj->_content = NULL;
    else if(obj->_type == AMF3DataType::String){
        obj->_content = ReadAMF3String();
    }
    else if(obj->_type == AMF3DataType::True)obj->_content = NULL;
    else if(obj->_type == AMF3DataType::False)obj->_content = NULL;
    else if(obj->_type == AMF3DataType::Integer) {
        obj->_content = new wxInt32(ReadAMF3IntegerData());
    }
    else if(obj->_type == AMF3DataType::Double) {
        obj->_content = new wxDouble(ReadDouble());
    }
    else if(obj->_type == AMF3DataType::Array) {
        AMFObject *amfo = ReadAMF3Array();
        obj->_type = AMF3DataType::Null;
        delete obj;
        return amfo;
    }
    else if(obj->_type == AMF3DataType::Object) {
        obj->_content = ReadAMF3Object();
    }
    else throw wxString::Format(_T("尚未支援AMF3的'%d'資料型態讀取"), obj->_type);

    return obj;
}

vector<AMFObject*> *AMFReader::ReadAMFArray()
{
    vector<AMFObject*> *list = new vector<AMFObject*>();
    wxUint32 length = ReadUInt32();
    for(wxUint32 i = 0; i < length; i++) {
        AMFObject *obj = ReadAMFData();
        list->push_back(obj);
    }
    return list;
}

AMFObject* AMFReader::ReadAMF3Array()
{
    wxInt32 handle = ReadAMF3IntegerData();
    bool _inline = ((handle & 1) != 0); handle = handle >> 1;
    AMFObject *obj = new AMFObject();
    obj->_amf3 = true;
    if (_inline){
        Hashtable *hashtable = new Hashtable();
        wxString *key = ReadAMF3String();
        while (!key->IsEmpty())
        {
            (*hashtable)[*key] = ReadAMF3Data();
            key->Clear();
            delete key;
            key = ReadAMF3String();
        }
        key->Clear();
        delete key;
        if(hashtable->size() == 0) {
        	delete hashtable;
            vector<AMFObject*> *olist = new vector<AMFObject*>();
            for (int i = 0; i < handle; i++) {
                olist->push_back(ReadAMF3Data());
            }
            obj->_type = wxByte(AMF3DataType::Array);
            obj->_content = olist;
        }
        else {
            for (int i = 0; i < handle; i++) {
               (*hashtable)[wxString::Format(_T("%d"), i)] = ReadAMF3Data();
            }
            obj->_type = wxByte(AMF3DataType::Object);
            obj->_content = hashtable;
        }
        return obj;
    }
    else throw _("未支援物件參照!!");
}

Hashtable *AMFReader::ReadAMF3Object()
{
    Hashtable *list = new Hashtable();
    int handle = ReadAMF3IntegerData();
    bool _inline = ((handle & 1) != 0);
    handle = handle >> 1;
    if (!_inline) throw _("ReadAMF3Object未支援!inline");
    else {
        TryClassDefinition(handle);
        wxString *key = ReadAMF3String();
        while (!key->IsEmpty())
        {
            (*list)[*key] = ReadAMF3Data();
            key->Clear();
            delete key;
            key = ReadAMF3String();
        }
        key->Clear();
        delete key;
    }
    return list;
}

void AMFReader::TryClassDefinition(wxInt32 handle)
{
    bool inlineClassDef = ((handle & 1) != 0);
    handle = handle >> 1;
    if (inlineClassDef)
    {
        wxString *s = ReadAMF3String();
        s->Clear();
        delete s;
        bool externalizable = ((handle & 1) != 0); handle = handle >> 1;
        //bool dynamic = ((handle & 1) != 0);
        handle = handle >> 1;
        if (handle > 0 || externalizable) throw wxString::Format(_("未支援-TryClassDefinition(%d)"), handle);
   }
}

wxString *AMFReader::ReadAMF3String()
{
    int handle = ReadAMF3IntegerData();
    bool _inline = ((handle & 1) != 0);
    handle = handle >> 1;
    if (_inline)
    {
        wxString *str = new wxString();
        int length = handle;
        if (length == 0) {
            return str;
        }
        char buff[length];
        _input->Read(buff, length);
        str->Append(wxString::FromUTF8(buff, length));
        _stringReference.Add(*str);
        return str;
    }
    else
    {
        if (handle >= _stringReference.GetCount()) return new wxString();
        return new wxString(_stringReference.Item(handle));
    }
}

wxInt32 AMFReader::ReadAMF3IntegerData()
{
    unsigned char buff;
    _input->Read(&buff, 1);
    int acc = buff;
    if (acc < 128) return acc;
    else {
        int tmp;
        acc = (acc & 0x7f) << 7;
        _input->Read(&buff, 1);
        tmp = buff;
        if (tmp < 128) acc = acc | tmp;
        else {
            acc = (acc | tmp & 0x7f) << 7;
            _input->Read(&buff, 1);
            tmp = buff;
            if (tmp < 128) acc = acc | tmp;
            else {
                acc = (acc | tmp & 0x7f) << 8;
                _input->Read(&buff, 1);
                tmp = buff;
                acc = acc | tmp;
            }
        }
    }
    int mask = 1 << 28; // mask
    int r = -(acc & mask) | acc;
    return r;
}

wxUint16 AMFReader::ReadUInt16()
{
    wxUint16 v = 0;
    vector<wxByte> tmp = ReadIntegerBytes(2);
    for(unsigned int i = 0; i < tmp.size(); i++) {
         v = v << 8 | tmp[i];
    }
    tmp.clear();
    return v;
}

wxUint32 AMFReader::ReadUInt32()
{
    wxUint32 v = 0;
    vector<wxByte> tmp = ReadIntegerBytes(4);
    for(unsigned int i = 0; i < tmp.size(); i++) {
         v = v << 8 | tmp[i];
    }
    tmp.clear();
    return v;
}

wxDouble AMFReader::ReadDouble()
{
    wxByte b[8] = {0};
    vector<wxByte> tmp = ReadIntegerBytes(8);
    for(int i = tmp.size() - 1; i >= 0; i--) {
         b[7 - i] = tmp[i];
    }
    tmp.clear();
    wxDouble d = *(wxDouble *)b;
    return d;
}

vector<wxByte> AMFReader::ReadIntegerBytes(int s)
{
    wxByte *tmp = new wxByte[s];
    _input->Read(tmp, s);
    vector<unsigned char> t(tmp, tmp + s);
    delete [] tmp;
    return t;
}

wxString AMFReader::ReadString()
{
    wxUint16 len = ReadUInt16();
    if (len == 0) return wxEmptyString;
    char tmp[len];
    _input->Read(tmp, len);
    return wxString::FromUTF8(tmp, len);
}

// namespace AMF
